package com.jiangyg.mall.db.hibernate.dao;

import com.jiangyg.mall.db.SQLUtils;
import com.jiangyg.mall.db.datasource.DataSourceContextHolder;
import com.jiangyg.mall.db.hibernate.HibernateSupport;
import com.jiangyg.mall.db.hibernate.mapper.SQLMapper;
import com.jiangyg.mall.db.hibernate.transform.AliasToCamelCaseMapResultTransformer;
import org.apache.commons.collections.CollectionUtils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.internal.NativeQueryImpl;
import org.springframework.util.Assert;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * 类描述：通用的数据操作Dao
 *
 * @author jiangyg
 * @version 2.0
 * @date 2020-10-18
 */
@SuppressWarnings("unchecked")
public interface GenericDao {

    /**
     * 功能描述：获取session
     *
     * @return 返回session
     */
    Session session();

    /**
     * 功能描述：获取SessionFactory
     *
     * @return 返回SessionFactory
     */
    SessionFactory sessionFactory();

    /**
     * 功能描述：获取数据库链接
     *
     * @return 返回数据库链接
     */
    Connection connection() throws SQLException;

    /**
     * 功能描述：获取sqlMapper对象
     *
     * @return 返回sqlMapper对象
     */
    SQLMapper mapper();

    /**
     * 功能描述：根据namedQuery获取执行SQL字符串
     *
     * @param namedQuery  SQL名称
     * @param searchModel 查询模型
     * @return 执行SQL字符串
     */
    default String getSqlByNamed(final String namedQuery, final Object searchModel) {
        return mapper().getSqlByNamed(namedQuery, searchModel);
    }

    /**
     * 功能描述：查询list<map>结果集
     *
     * @param namedQuery  SQL名称
     * @param searchModel 查询模型
     * @return Map结果集
     */
    default List<Map<String, Object>> queryListMapByNamedQuery(final String namedQuery, final Object searchModel) {
        final String queryString = this.getSqlByNamed(namedQuery, searchModel);
        return this.queryListMapByQueryString(queryString, searchModel);
    }

    /**
     * 功能描述：查询list<T>结果集
     *
     * @param namedQuery  SQL名称
     * @param searchModel 查询模型
     * @param clazz       结果集clazz
     * @return 结果集
     */
    default <T> List<T> queryListByNamedQuery(final String namedQuery, final Object searchModel, Class<T> clazz) {
        final String queryString = this.getSqlByNamed(namedQuery, searchModel);
        return this.queryListByQueryString(queryString, searchModel, clazz);
    }

    /**
     * 功能描述：查询单条结果
     *
     * @param namedQuery  SQL名称
     * @param searchModel 查询模型
     * @param clazz       结果集clazz
     * @return bean
     */
    default <T> T queryDataByNamedQuery(final String namedQuery, final Object searchModel, Class<T> clazz) {
        final List<T> list = this.queryListByNamedQuery(namedQuery, searchModel, clazz);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    /**
     * 功能描述：查询单条结果
     *
     * @param namedQuery  SQL名称
     * @param searchModel 查询模型
     * @return Map
     */
    default Map<String, Object> queryMapByNamedQuery(final String namedQuery, final Object searchModel) {
        final List<Map<String, Object>> list = this.queryListMapByNamedQuery(namedQuery, searchModel);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    /**
     * 功能描述：统计总数
     *
     * @param namedQuery  SQL名称
     * @param searchModel 查询模型
     * @return 统计值
     */
    default Integer queryCountByNamedQuery(final String namedQuery, final Object searchModel) {
        final String queryString = this.getSqlByNamed(namedQuery, searchModel);
        return queryCountByQueryString(queryString, searchModel);
    }

    /**
     * 功能描述：统计总数
     *
     * @param queryString SQL字符串
     * @param searchModel 查询模型
     * @return 统计值
     */
    default Integer queryCountByQueryString(final String queryString, final Object searchModel) {
        return HibernateSupport.queryCountByQueryString(queryString, searchModel, session());
    }

    /**
     * 功能描述：查询list<T>结果集
     *
     * @param queryString 查询SQL字符串
     * @param searchModel 查询模型
     * @param clazz       结果集clazz
     * @return List结果集
     */
    default <T> List<T> queryListByQueryString(final String queryString, final Object searchModel, Class<T> clazz) {
        return HibernateSupport.queryListByQueryString(queryString, searchModel, clazz, session());
    }

    /**
     * 功能描述：查询list<map>结果集
     *
     * @param queryString 查询SQL字符串
     * @param searchModel 查询模型
     * @return Map结果集
     */
    default List<Map<String, Object>> queryListMapByQueryString(final String queryString, final Object searchModel) {
        return HibernateSupport.queryListMapByQueryString(queryString, searchModel, session());
    }

    /**
     * 功能描述：分页查询list<bean>结果集
     *
     * @param namedQuery  查询命名
     * @param searchModel 查询模型
     * @param pageNum     页码
     * @param pageSize    页大小
     * @return List结果集
     */
    default <T> List<T> queryListByNamedQuery(final String namedQuery, final Object searchModel, int pageNum, int pageSize, Class<T> clazz) {
        final String queryString = this.getSqlByNamed(namedQuery, searchModel);
        return this.queryListByQueryString(queryString, searchModel, pageNum, pageSize, clazz);
    }

    /**
     * 功能描述：分页查询list<T>结果集
     *
     * @param queryString 查询SQL字符串
     * @param searchModel 查询模型
     * @param pageNum     页码
     * @param pageSize    页大小
     * @return List结果集
     */
    default <T> List<T> queryListByQueryString(final String queryString, final Object searchModel, int pageNum, int pageSize, Class<T> clazz) {
        return HibernateSupport.queryListByQueryString(queryString, searchModel, clazz, pageNum, pageSize, session());
    }

    /**
     * 功能描述：分页查询list<map>结果集
     *
     * @param namedQuery  命名查询
     * @param searchModel 查询模型
     * @param pageNum     页码
     * @param pageSize    页大小
     * @return List结果集
     */
    default List<Map<String, Object>> queryListMapByNamedQuery(final String namedQuery, final Object searchModel, int pageNum, int pageSize) {
        final String queryString = this.getSqlByNamed(namedQuery, searchModel);
        return HibernateSupport.queryListMapByQueryString(queryString, searchModel, pageNum, pageSize, session());
    }

    /**
     * 功能描述：分页查询list<map>结果集
     *
     * @param queryString 查询SQL字符串
     * @param searchModel 查询模型
     * @param pageNum     页码
     * @param pageSize    页大小
     * @return List结果集
     */
    default List<Map<String, Object>> queryListMapByQueryString(final String queryString, final Object searchModel, int pageNum, int pageSize) {
        return HibernateSupport.queryListMapByQueryString(queryString, searchModel, pageNum, pageSize, session());
    }

    /**
     * 功能描述：查询list<T>结果集
     *
     * @param namedQuery  查询命名
     * @param searchModel 查询模型
     * @param maxResults  最大结果集
     * @return List结果集
     */
    default <T> List<T> queryListByNamedQuery(final String namedQuery, final Object searchModel, int maxResults, Class<T> clazz) {
        final String queryString = this.getSqlByNamed(namedQuery, searchModel);
        return this.queryListByQueryString(queryString, searchModel, maxResults, clazz);
    }

    /**
     * 功能描述：查询list<T>结果集
     *
     * @param queryString 查询SQL字符串
     * @param searchModel 查询模型
     * @param maxResults  最大结果集
     * @return 结果集
     */
    default <T> List<T> queryListByQueryString(final String queryString, final Object searchModel, int maxResults, Class<T> clazz) {
        return HibernateSupport.queryListByQueryString(queryString, searchModel, maxResults, clazz, session());
    }

    /**
     * 功能描述：查询list<map>结果集
     *
     * @param queryString 查询SQL字符串
     * @param searchModel 查询模型
     * @param maxResults  最大结果集
     * @return 结果集
     */
    default List<Map<String, Object>> queryListMapByQueryString(final String queryString, final Object searchModel, int maxResults) {
        return HibernateSupport.queryListMapByQueryString(queryString, searchModel, maxResults, session());
    }

    /**
     * 功能描述：查询map结果集
     *
     * @param queryString 查询SQL字符串
     * @param searchModel 查询模型
     * @return Map
     */
    default Map<String, Object> queryMapByQueryString(final String queryString, final Object searchModel) {
        final List<Map<String, Object>> list = this.queryListMapByQueryString(queryString, searchModel);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    /**
     * 功能描述：执行SQL
     *
     * @param namedQuery SQL名称
     * @param dto        数据DTO
     * @return 执行条数
     */
    default int executeUpdate(final String namedQuery, final Object dto) {
        final String executeSQL = this.getSqlByNamed(namedQuery, dto);
        return this.executeUpdateString(executeSQL, dto);
    }

    /**
     * 功能描述：执行SQL
     *
     * @param excuteSQL 执行SQL字符串
     * @param dto       数据DTO
     * @return 执行条数
     */
    default int executeUpdateString(final String excuteSQL, final Object dto) {
        return HibernateSupport.executeUpdateString(excuteSQL, dto, session());
    }

    /**
     * 功能描述：数据同步方法，默认每次同步条数1000
     *
     * @param source      待同步数据源
     * @param target      目标数据源
     * @param namedQuery  待同步的查询namedQuery
     * @param namedSave   执行插入的namedSave（插入参数名必须和查询语句列名驼峰一致）
     * @param searchModel 查询SQL的参数（插入SQL也可以根据此参数进行velocity语法判断）
     * @return 数组[成功条数, 失败条数]
     * @throws Exception 异常
     * @author jiangyg
     * @date 2021-03-04
     */
    default int[] dataSync(String source, String target, String namedQuery, String namedSave, final Object searchModel) throws Exception {
        return dataSync(source, target, namedQuery, namedSave, searchModel, 1000);
    }

    /**
     * 功能描述：数据同步方法
     *
     * @param source      待同步数据源
     * @param target      目标数据源
     * @param namedQuery  待同步的查询namedQuery
     * @param namedSave   执行插入的namedSave（插入参数名必须和查询语句列名驼峰一致）
     * @param searchModel 查询SQL的参数（插入SQL也可以根据此参数进行velocity语法判断）
     * @param rows        每次同步条数
     * @return 数组[成功条数, 失败条数]
     * @throws Exception 异常
     * @author jiangyg
     * @date 2021-03-04
     */
    default int[] dataSync(String source, String target, String namedQuery, String namedSave, final Object searchModel, int rows) throws Exception {
        // TODO 需要测试
        Assert.isNull(DataSourceContextHolder.current(), "此方法不能在事物中执行！");
        Session session = null;
        Connection conn = null;
        PreparedStatement statement = null;
        try {
            // 目标数据源，注意必须先获取目标数据源的conn，在获取session
            DataSourceContextHolder.set(target);
            conn = connection();
            // 待同步数据源
            DataSourceContextHolder.set(source);
            session = sessionFactory().openSession();
            // 开始同步数据处理
            int error = 0;
            int success = 0;
            int pageNum = 1;
            List<Map<String, Object>> dataList;
            // 查询SQL
            final String queryString = this.getSqlByNamed(namedQuery, searchModel);
            // 保存SQL、参数解析和去除命名参数的执行SQL
            final String saveString = this.getSqlByNamed(namedSave, searchModel);
            final List<String> params = SQLUtils.parseSQLParams(saveString);
            String insertSQL = saveString;
            for (String param : params) {
                insertSQL = insertSQL.replaceAll(":" + param, "?");
            }
            do {
                // 查询指定页码的数据
                final NativeQueryImpl<?> query = session.createSQLQuery(queryString).unwrap(NativeQueryImpl.class);
                HibernateSupport.setProperties(query, searchModel, session);
                query.setResultTransformer(AliasToCamelCaseMapResultTransformer.INSTANCE);
                query.setFirstResult((pageNum - 1) * rows);
                query.setMaxResults(rows);
                dataList = (List<Map<String, Object>>) query.list();
                // 没有查询到待同步数据则跳出循环
                if (CollectionUtils.isEmpty(dataList)) {
                    continue;
                }
                // 批量执行insert
                statement = conn.prepareStatement(insertSQL);
                // 批量设置参数
                for (Map<String, Object> data : dataList) {
                    int index = 1;
                    for (String param : params) {
                        Object value = data.get(param);
                        // 此处有坑，MySQL设置Character类型参数为null字符串
                        if (value instanceof Character) {
                            value = value.toString();
                        }
                        statement.setObject(index, value);
                        ++index;
                    }
                    statement.addBatch();
                }
                // 批量提交并计数
                int[] results = statement.executeBatch();
                for (int result : results) {
                    if (result == -2) {
                        ++success;
                    } else {
                        ++error;
                    }
                }
                statement.clearBatch();
                ++pageNum;
            } while (CollectionUtils.isNotEmpty(dataList));
            return new int[]{success, error};
        } finally {
            // 清空数据源上下文
            DataSourceContextHolder.clean();
            // 关闭资源
            if (session != null) {
                session.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (conn != null) {
                conn.close();
            }
        }
    }

}
